﻿
using System;
using System.Linq;
using System.Collections.Generic;

using Gola.VN.Scb.Web.Dal;
using Gola.VN.Scb.Web.Common;
using Gola.VN.Scb.Web.Common.Entities;

namespace Gola.VN.Scb.Web.Bll
{
    internal class ProductBiz : BaseBiz
    {
        private List<CategoryEntity> _DisplayingCategories;
        internal ProductBiz(MainDao mainDao)
            : base(mainDao)
        {
            _DisplayingCategories = new List<CategoryEntity>();
        }

        internal int InsertProduct(ProductEntity product)
        {
            // 0. initialize package
            DaoPackage package = new DaoPackage();

            // 1. insert into database
            package.Channel = ChannelType.Sql;
            package.Input = product;
            package.Event = DaoEvent.InsertProduct;
            _mainDao.Execute(package);

            // 2. insert into memory
            int result = System.Convert.ToInt32(package.Output);
            if (result > 0)
            {
                package.Input = product;
                package.Channel = ChannelType.Memory;
                package.Event = DaoEvent.InsertProduct;
                _mainDao.Execute(package);
            }

            return result;
        }

        internal void InitializeData()
        {
            DaoPackage package = new DaoPackage();

            package.Channel = ChannelType.Sql;
            package.Event = DaoEvent.GetProducts;
            _mainDao.Execute(package);

            package.Channel = ChannelType.Memory;
            package.Event = DaoEvent.SetProducts;
            package.Input = package.Output;
            _mainDao.Execute(package);
        }

        internal ProductEntity GetProductById(int id)
        {
            IEnumerable<ProductEntity> products = GetProducts();
            return products.First(q => q.Id == id);
        }

        internal IEnumerable<ProductEntity> GetProducts()
        {
            DaoPackage package = new DaoPackage();
            package.Channel = ChannelType.Memory;
            package.Event = DaoEvent.GetProducts;
            _mainDao.Execute(package);

            return package.Output as IEnumerable<ProductEntity>;
        }

        internal List<ProductEntity> GetProductsByTitle(string title)
        {
            IEnumerable<ProductEntity> products = GetProducts();
            CategoryBiz categoryBiz = new CategoryBiz(_mainDao);
            IEnumerable<CategoryEntity> categories = categoryBiz.GetCategoriesAsFlat();
            IEnumerable<int> categroryIds = from c in categories where c.Name.Contains(title) select c.Id;

            return (from p in products where (p.Title.Contains(title) || categroryIds.Contains(p.CategoryId)) select p).ToList();
        }

        internal List<ProductEntity> GetProductsByCategoryId(int categoryId, bool orderByViewCount = false)
        {
            IEnumerable<ProductEntity> products = GetProducts();

            return orderByViewCount ? (from p in products where (p.CategoryId == categoryId) select p).OrderBy(q => q.ViewCount).ToList()
                : (from p in products where (p.CategoryId == categoryId) select p).OrderBy(q => q.Title).ToList();
        }

        internal List<ProductEntity> GetProductsByProviderIdAndCategoryName(string categoryName, int providerId)
        {
            IEnumerable<ProductEntity> products = GetProducts();
            CategoryBiz categoryBiz = new CategoryBiz(_mainDao);
            IEnumerable<CategoryEntity> categories = categoryBiz.GetCategoriesAsFlat();

            CategoryEntity category = string.IsNullOrEmpty(categoryName) ? null :
                categories.FirstOrDefault(c => c.Name == categoryName);

            if (category != null)
                return (from p in products where (p.CategoryId == category.Id && p.ProviderId == providerId) select p).ToList();
            else
                return (from p in products where (p.ProviderId == providerId) select p).ToList();
        }

        internal List<ProductEntity> GetProductsByProductTitle(int productId, string productTitle)
        {
            IEnumerable<ProductEntity> products = GetProducts();

            return (from p in products where (p.Title == productTitle && p.Id != productId) orderby p.Price ascending select p).ToList();
        }

        internal List<ProductEntity> GetOthersProductByTitle(string title, List<int> exclusiveroductIds, int pageIndex)
        {
            IEnumerable<ProductEntity> products = GetProducts();
            var selectedProducts =
                (from p in products
                 where IsMatch(p.Title, title) && !exclusiveroductIds.Contains(p.Id)
                 orderby p.Price ascending
                 select p).ToList();

            return new PaginatedList<ProductEntity>(selectedProducts, pageIndex);
        }

        internal List<ProductEntity> GetOthersProductByCategoryId(int categoryId, List<int> exclusiveroductIds, int pageIndex)
        {
            IEnumerable<ProductEntity> products = GetProducts();
            var selectedProducts = (from p in products
                                    where p.CategoryId == categoryId && !exclusiveroductIds.Contains(p.Id)
                                    orderby p.Price ascending
                                    select p).ToList();

            return new PaginatedList<ProductEntity>(selectedProducts, pageIndex);
        }

        /// <summary>
        /// Increase view count of the product when user views it
        /// </summary>
        /// <param name="productId"></param>
        internal void UpdateProductViewCount(int productId)
        {
            var product = GetProductById(productId);
            product.ViewCount++;
            //store db
            // 0. initialize package
            DaoPackage package = new DaoPackage();

            // 1. insert into database
            package.Channel = ChannelType.Sql;
            package.Input = product;
            package.Event = DaoEvent.UpdateProduct;
            _mainDao.Execute(package);

            // 2. insert into memory
            int result = System.Convert.ToInt32(package.Output);
            if (result > 0)
            {
                package.Input = product;
                package.Channel = ChannelType.Memory;
                package.Event = DaoEvent.UpdateProduct;
                _mainDao.Execute(package);
            }
        }

        /// <summary>
        /// Rule to get host categories:
        /// 1. We get 4 top category
        /// 2. this is most category that user searches or views in the system.
        /// 3. If there is not any record in search history System will get default 4 category 'Tivi LCD'(1002), 'Tu Lanh'(1101),
        /// 'Dieu hoa'(1103), 'Dien thoai di dong'(1501)
        /// </summary>
        /// <returns></returns>
        internal List<CategoryEntity> GetHostCategories()
        {
            const int NumberItemHostCategories = 4;
            var result = new List<CategoryEntity>(NumberItemHostCategories);
            CategoryBiz categoryBiz = new CategoryBiz(_mainDao);
            //0. Get data from Search History table
            DaoPackage package = new DaoPackage();
            package.Channel = ChannelType.Sql;
            package.Event = DaoEvent.GetSearchHistories;
            _mainDao.Execute(package);

            var searchHistory = package.Output as List<SearchHistoryEntity>;
            //1. See, if there is not any search history
            // if (searchHistory == null)
            {
                result.Add(categoryBiz.GetCategoryById(1201));
                result.Add(categoryBiz.GetCategoryById(1203));
                result.Add(categoryBiz.GetCategoryById(1001));
                result.Add(categoryBiz.GetCategoryById(1004));
            //    return result;
            }

            //2. Get 4 top categories from search history
            //todo:HuyNN

            //add host category to displaying categories
            _DisplayingCategories.AddRange(result);
            return result;
        }

        internal CategoryEntity GetRandomCategory()
        {
            CategoryBiz categoryBiz = new CategoryBiz(_mainDao);
            //get all sub categories which has products.
            var categroies = categoryBiz.GetCategoriesAsFlat().Where(q => q.Id > 99).OrderBy(p => p.Name).Where(q => !GetHostCategories().Contains(q));

            var indexRandom = GetRandom(categroies.Count());
            var randomCategory = categroies.ElementAtOrDefault(indexRandom);

            randomCategory = categroies.ElementAtOrDefault(indexRandom);

            return randomCategory;
        }

        private int GetRandom(int maxNumber, int minNumber = 0)
        {
            Random random = new Random();
            return random.Next(minNumber, maxNumber);
        }

        private bool IsMatch(string str1, string str2)
        {
            if (str1 == str2)
                return true;

            string[] words1 = str1.Split(new char[] { ' ' });
            string[] words2 = str2.Split(new char[] { ' ' });
            int matchedNumber = 0;
            int length = words1.Length;

            if (words1.Length > words2.Length)
            {
                length = words2.Length;

                foreach (string word in words2)
                    matchedNumber += str1.Contains(word) ? 1 : 0;
            }
            else
            {
                foreach (string word in words1)
                    matchedNumber += str2.Contains(word) ? 1 : 0;
            }

            if (matchedNumber > 0)
                return matchedNumber > 0.8 * length ? true : false;

            return false;
        }
    }
}